#encoding = utf-8

#怪物基类

# 导入模块
import pygame
import random
import math

'''
BasicsMonster类，继承于pygame.sprite.Sprite
'''
class BasMonster(pygame.sprite.Sprite):
    "BasicsMonster怪物基类，继承于pygame.sprite.Sprite，编写了基本的属性，随机移动方法，发现玩家方法，\
    发现玩家跟随玩家的方法，攻击方法，会话方法等"

    #怪物列表，用于存放怪物使用
    lstMonster = []

    '''
    构造函数
    '''
    def __init__(self, win, winSize, player, strImagePath, strPicture, strName='小怪兽', views=100, attackDistance=0):
        pygame.sprite.Sprite.__init__(self)
        # 公共基础属性
        self.player = player  # 玩家对象
        "player属性，传递玩家对象"
        self.win = win  # 窗体对象
        "win属性，传递游戏窗体对象"
        self.winSize = winSize  # 窗体大小
        "winSize属性，传递窗体的大小"
        self.name = strName  # 怪物的名字
        "name属性，怪物名称"
        self.imagePath = strImagePath  # 造型路径
        "imagePath属性，怪物造成路径"
        self.imagePicture = strPicture
        "imagePicture属性，怪物的图片"
        self.attackType = 'combat'  # 攻击类型
        "attackType属性，怪物攻击类型，默认为combat近战，longrange远程"
        self.baseAttack = 30  # 基础攻击力
        "baseAttack属性，基础攻击力"
        self.baseDefense = 2  # 基础防御力
        "baseDefense属性，基础防御力"
        self.baseSpeed = 1  # 基础速度
        "baseSpeed属性，基础速度"
        self.blood = 1000  # 血量
        "blood属性，血量"
        self.views = views  # 视野
        "views属性，怪物视野范围"
        self.attackDistance = attackDistance  # 基础攻击距离
        "attackDistance属性，攻击距离，默认为玩家的宽度"
        self.attackInterval = 120  # 攻击频率间隔
        "attackInterval属性，攻击频率间隔时间"
        self.bloodHeight = 6  # 血条高度
        "bloodHeight属性，血条显示于怪物图片顶部的相对高度"
        self.width = 40
        "怪物的宽度"
        self.height = 40
        "怪物的高度"
        self.clear = False
        "是否清除怪物"

        #私有属性
        self.fonts = "fonts/msyh.ttf"
        "fonts使用的字体"
        self.fontSize = 12  # 字体大小
        "fontSize属性，怪物名字字体大小"
        self.image = ''  # 造型
        "image属性，怪物的形象"
        self.fullImage = '' #完整造型
        "fullImage属性，转换后的图片文件"
        self.fullBlood = self.blood #满血值
        "fullBlood属性，用于存放满血额血量"
        self.reduceBloodEffect = [] #掉血效果
        "reduceBloodEffect属性，用于播放掉血效果"
        self.moveX = 0  # 随机移动目标X位置
        "moveX属性，随机移动目的地X坐标"
        self.moveY = 0  # 随机移动目标Y位置
        "moveY属性，随机移动目的地Y坐标"
        self.moveStop = random.randint(30, 180)  # 停留时间，半秒到3秒随机
        "moveStop属性，怪物游走随机停留时间"
        self.start = True  # 判断是否开始移动
        "start属性，布尔值，初始化状态，用于判断是否是初始化"
        self.findPlayer = False  # 判断是否发现玩家
        "findPlayer属性，布尔值，判断是否发现玩家"
        self.attackStat = False  # 是否处于攻击状态
        "attackStat属性，布尔值，是否处于攻击状态"
        self.attackTimes = 0  # 攻击计时
        "attackTimes属性，攻击间隔时间计时"
        self.changeImagesTimer = 10  # 造型切换计时
        "changeImagesTimer属性，日常造型切换的计时"
        self.changeImageCurrent = 0  # 造形编号，从0到7
        "changeImageCurrent属性，当前造型编号，变更数字，变更造型，从1到8"
        self.rect = None
        "当前图片的rect值"
        self.dieOrAttack = 0    #0表示行走状态，1表示攻击状态，2表示死亡状态
        "是否死亡或者攻击模式"

        #下面这里是初始化一些数据
        self.fullImage = pygame.image.load(self.imagePath + self.imagePicture).convert_alpha()
        self.image = self.fullImage.subsurface((0, 0, self.width, self.height))
        if self.attackDistance == 0:
            self.attackDistance = self.player.rect.width
        # 生成随机数，用于确定怪物随机坐标
        self.rect = self.image.get_rect()
        self.rect.left = random.randint(0, self.winSize[0] - self.rect.width)
        self.rect.top = random.randint(self.rect.height + self.bloodHeight, self.winSize[1] - self.rect.height)
        # 生成随机方向,8个方向，顺时针旋转，0上，1右上，2右，3右下，4下，5左下，6左，7左上
        self.randMoveDirection = random.randint(0, 7)
        # 生成随机移动距离
        self.randMoveRange = random.randint(self.rect.height, self.rect.height * 3)


    '''
    addMonster类方法，添加怪物
    参数：
    objMonster：怪物实例
    '''
    @classmethod
    def addMonster(cls, objMonster):
        "向基类的列表中添加怪物实例"
        cls.lstMonster.append(objMonster)


    '''
    类方法，移除怪物
    参数：
    objMonster：怪物实例
    '''
    @classmethod
    def removeMonster(cls, objMonster):
        "删除基类的列表中的怪物实例"
        del cls.lstMonster[objMonster]


    '''
    类方法：setAttribute
    参数：
    *attribute：字典类型，其值都为数字
    '''
    def setAttribute(self, attribute):
        "setAttribute方法，用于设置一些常用的属性值"
        self.baseAttack = attribute.get('baseAttack')
        self.baseDefense = attribute.get('baseDefense')
        self.baseSpeed = attribute.get('baseSpeed')
        self.blood = attribute.get('blood')
        self.fullBlood = self.blood
        self.attackInterval = attribute.get('attackInterval')
        self.bloodHeight = attribute.get('bloodHeight')
        self.width = attribute.get('width', 40)
        self.height = attribute.get('height', 40)
        self.rect.width = self.width
        self.rect.height = self.height


    '''
    播放怪物图片
    参数：
    direction：方向
    '''
    def playMonsterImage(self, direction):
        # normal = 0
        # if self.dieOrAttack:
        #     if direction == 16 and self.changeImageCurrent == 7:
        #         self.clear = True
        #     else:
        #         normal = 8
        #print(self.dieOrAttack, direction)
        if self.dieOrAttack == 2:
            if direction == 8 and self.changeImageCurrent == 7:
                self.clear = True
        #elif self.dieOrAttack == 1:

        top = direction * self.height

        left = self.changeImageCurrent * self.width
        rect = (left, top, self.width, self.height)
        # 当为0的时候就更换
        if self.changeImagesTimer == 0:
            self.image = self.fullImage.subsurface(rect)
            self.changeImageCurrent += 1
            if self.changeImageCurrent > 7:
                self.changeImageCurrent = 0
            self.changeImagesTimer = 10
        else:
            self.changeImagesTimer -= 1



    '''
    判断移动的地址是否超出范围，超出了就按照最小的坐标移动
    '''
    def checkPosition(self):
        "根据方向确认移动目标左边X和Y是否超出范围，超出了就按照最小的坐标移动"
        if self.randMoveDirection == 0:
            self.moveY, self.moveX = self.rect.top - self.randMoveRange, self.rect.left
        elif self.randMoveDirection == 1:
            self.moveY, self.moveX = self.rect.top - self.randMoveRange, self.rect.left + self.randMoveRange
        elif self.randMoveDirection == 2:
            self.moveY, self.moveX = self.rect.top, self.rect.left + self.randMoveRange
        elif self.randMoveDirection == 3:
            self.moveY, self.moveX = self.rect.top + self.randMoveRange, self.rect.left + self.randMoveRange
        elif self.randMoveDirection == 4:
            self.moveY, self.moveX = self.rect.top + self.randMoveRange, self.rect.left
        elif self.randMoveDirection == 5:
            self.moveY, self.moveX = self.rect.top + self.randMoveRange, self.rect.left - self.randMoveRange
        elif self.randMoveDirection == 6:
            self.moveY, self.moveX = self.rect.top, self.rect.left - self.randMoveRange
        else:
            self.moveY, self.moveX = self.rect.top - self.randMoveRange, self.rect.left - self.randMoveRange
        if self.moveY < self.rect.height + self.bloodHeight:
            self.moveY = self.rect.height + self.bloodHeight
        if self.moveY > self.winSize[1] - self.rect.height:
            self.moveY = self.winSize[1] - self.rect.height
        if self.moveX < 0:
            self.moveX = 0
        if self.moveX > self.winSize[0] - self.rect.width:
            self.moveX = self.winSize[0] - self.rect.width


    '''
    重置随机参数
    '''
    def resetRand(self):
        "重置怪物随机参数"
        if self.rect.top == self.moveY and self.rect.left == self.moveX:
            self.start = True
            self.randMoveDirection = random.randint(0, 7)
            self.moveStop = random.randint(30, 180)
            self.randMoveRange = random.randint(self.rect.height, self.rect.height * 3)
            self.checkPosition()


    '''
    以怪物为圆心计算到玩家的角度，0到360度
    参数：
    differX：玩家到怪物的X轴差值，为带符号的整数
    differY：玩家到怪物的Y轴差值，为带符号的整数
    '''
    def mathAngle(self, differX, differY):
        "计算到玩家的角度，传递的differX为以怪物为圆心，玩家到怪物的X轴的差值，需要带符号的整数，differY相同，返回值为0到360度的度数"
        angle = 360 - math.atan2(differY, differX) * (180 / math.pi)  # 角度
        if angle >= 360:
            angle -= 360
        return angle


    '''
    根据角度计算方向，计算玩家在怪物的那个方向
    参数：
    differX：玩家到怪物的X轴差值，为带符号的整数
    differY：玩家到怪物的Y轴差值，为带符号的整数
    '''
    def mathAngleToDirection(self, differX, differY):
        "计算玩家在怪物的那个方向，0为上，1为右上，按顺时针到7，返回值为方向数字"
        angle = self.mathAngle(differX, differY)
        if angle >= 23 and angle < 68:  # 右上
            direction = 1
        elif angle >= 68 and angle < 113:  # 上
            direction = 0
        elif angle >= 113 and angle < 158:  # 左上
            direction = 7
        elif angle >= 158 and angle < 203:  # 左
            direction = 6
        elif angle >= 203 and angle < 248:  # 左
            direction = 5
        elif angle >= 158 and angle < 293:  # 左
            direction = 4
        elif angle >= 293 and angle < 338:  # 左
            direction = 3
        else:
            direction = 2
        return direction


    '''
    怪物找到玩家后移动函数
    参数：
    direction：方向
    x：怪物与玩家的X坐标差值，为绝对值，无符号类型
    y：怪物与玩家的Y坐标差值，为绝对值，无符号类型
    z：怪物与玩家的距离
    '''
    def mathFindPlayerMove(self, direction, x, y, z):
        "发现玩家后向玩家移动的方法"
        self.randMoveDirection = direction
        if z > 40:
            mx = int(x / z + 0.5)
            my = int(y / z + 0.5)
        else:
            mx = 0
            my = 0
        if direction == 0:
            self.rect.top -= my
        elif direction == 1:
            self.rect.top -= my
            self.rect.left += mx
        elif direction == 2:
            self.rect.left += mx
        elif direction == 3:
            self.rect.top += my
            self.rect.left += mx
        elif direction == 4:
            self.rect.top += my
        elif direction == 5:
            self.rect.top += my
            self.rect.left -= mx
        elif direction == 6:
            self.rect.left -= mx
        elif direction == 7:
            self.rect.top -= my
            self.rect.left -= mx


    '''
    利用简单直角三角形计算怪物和玩家之间的距离
    '''
    def mathMonsterToPlayerSpacing(self):
        "利用简单直角三角形计算怪物和玩家之间的距离"
        x = self.player.rect.left - self.rect.left
        y = self.player.rect.top - self.rect.top
        z = math.sqrt(x * x + y * y)
        return z


    '''
    随机移动方法
    '''
    def move(self):
        "随机移动方法"
        self.resetRand()
        if self.randMoveDirection == 0:  # 上
            self.rect.top -= self.baseSpeed
            if self.rect.top < self.rect.height + self.bloodHeight:
                self.rect.top = self.rect.height + self.bloodHeight
        elif self.randMoveDirection == 1:
            self.rect.top -= self.baseSpeed
            self.rect.left += self.baseSpeed
            if self.rect.top < self.rect.height + self.bloodHeight:
                self.rect.top = self.rect.height + self.bloodHeight
            if self.rect.left > self.winSize[0] - self.rect.width:
                self.rect.left = self.winSize[0] - self.rect.width
        elif self.randMoveDirection == 2:  # 右
            self.rect.left += self.baseSpeed
            if self.rect.left > self.winSize[0] - self.rect.width:
                self.rect.left = self.winSize[0] - self.rect.width
        elif self.randMoveDirection == 3:
            self.rect.top += self.baseSpeed
            self.rect.left += self.baseSpeed
            if self.rect.top > self.winSize[1] - self.rect.height:
                self.rect.top = self.winSize[1] - self.rect.height
            if self.rect.left > self.winSize[0] - self.rect.width:
                self.rect.left = self.winSize[0] - self.rect.width
        elif self.randMoveDirection == 4:  # 下
            self.rect.top += self.baseSpeed
            if self.rect.top > self.winSize[1] - self.rect.height:
                self.rect.top = self.winSize[1] - self.rect.height
        elif self.randMoveDirection == 5:
            self.rect.top += self.baseSpeed
            self.rect.left -= self.baseSpeed
            if self.rect.top > self.winSize[1] - self.rect.height:
                self.rect.top = self.winSize[1] - self.rect.height
            if self.rect.left < 0:
                self.rect.left = 0
        elif self.randMoveDirection == 6:  # 左
            self.rect.left -= self.baseSpeed
            if self.rect.left < 0:
                self.rect.left = 0
        else:
            self.rect.top -= self.baseSpeed
            self.rect.left -= self.baseSpeed
            if self.rect.top < self.rect.height + self.bloodHeight:
                self.rect.top = self.rect.height + self.bloodHeight
            if self.rect.left < 0:
                self.rect.left = 0


    '''
    攻击方法
    '''
    def attack(self):
        "攻击方法"
        self.dieOrAttack = 1
        self.changeImagesTimer = 10
        self.changeImageCurrent = 0
        self.player.reduceBlood(self.baseAttack)
        if self.player.blood <= 0:
            self.player.blood = 0


    def drawReduceBlood(self):
        intI = 0
        clearList = []
        for b in self.reduceBloodEffect:
            bloodtext = pygame.font.Font(self.fonts, self.fontSize)
            bloodImage = bloodtext.render(str(b["reduceNum"]), True, (205, 0, 0))
            #intAlpha = int(b["step"] / b["all"] * 255)
            #print(intAlpha)
            #bloodImage.set_alpha(intAlpha)
            self.win.blit(bloodImage, (b["x"], b["y"]))
            if b["step"] != 0:
                b["step"] -= 1
                if b["dircetion"] == 0:
                    b["x"] -= 1
                elif b["dircetion"] == 1:
                    pass
                else:
                    b["x"] += 1
                b["y"] -= 1
            else:
                clearList.append(intI)
            intI += 1
        #删除到位置的掉血效果
        for i in clearList:
            del self.reduceBloodEffect[i]


    '''
    掉血方法
    参数：
    attack：玩家的攻击力
    '''
    def reduceBlood(self, attack):
        if self.blood <= 0:
            self.blood = 0
            if self.dieOrAttack != 2:
                self.dieOrAttack = 2
                self.changeImagesTimer = 10
                self.changeImageCurrent = 0
                self.playMonsterImage(8)
        else:
            bloodPoint = 0
            if attack - self.baseDefense < 0:
                bloodPoint += 1
            else:
                bloodPoint += attack - self.baseDefense
            self.blood -= bloodPoint
            #下面这里是把掉血动画的相关信息方到列表中
            dircetion = random.randint(0, 2)
            step = random.randint(21, 30)
            x = self.rect.left + int(self.rect.width / 2)
            y = self.rect.top - self.bloodHeight - 3
            lstEffect = {"dircetion": dircetion, "reduceNum": bloodPoint, "x": x, "y": y, "step": step}
            self.reduceBloodEffect.append(lstEffect)


    '''
    画出怪物名字
    '''
    def drawName(self):
        "画出怪物名字"
        top = self.rect.top - self.bloodHeight - self.fontSize - 4
        widthDifference = int((len(self.name) * self.fontSize - self.rect.width) / 2)
        left = self.rect.left - widthDifference
        textName = pygame.font.Font(self.fonts, self.fontSize)
        nameImage = textName.render(self.name, True, (255, 255, 255))
        self.win.blit(nameImage, (left, top))


    '''
    画出怪物血条
    '''
    def bloodStrip(self):
        "画出怪物血条"
        top = self.rect.top - self.bloodHeight - 1
        left = self.rect.left + 4
        width = self.rect.width - 8
        if self.blood <= 0:
            blood = 0
        else:
            blood = int(self.blood / self.fullBlood * width)

        bloodFrame = (0, 0, 0)  # 黑色
        bloodColor = (204, 0, 0)  # 红色
        bloodBack = (51, 51, 51)  # 深灰色

        pygame.draw.line(self.win, bloodFrame, [left, top], [left + width, top], 1)  # 上横线
        pygame.draw.line(self.win, bloodFrame, [left, top + self.bloodHeight - 2], [left + width, top + self.bloodHeight - 2], 1)  # 下横线
        pygame.draw.line(self.win, bloodFrame, [left - 1, top + 1], [left - 1, top + self.bloodHeight - 3], 1)  # 左竖线
        pygame.draw.line(self.win, bloodFrame, [left + width + 1, top + 1], [left + width + 1, top + self.bloodHeight - 3], 1)  # 右竖线
        pygame.draw.line(self.win, bloodBack, [left, top + (self.bloodHeight - 2) / 2], [left + width, top + (self.bloodHeight - 2) / 2], self.bloodHeight - 3)
        pygame.draw.line(self.win, bloodColor, [left, top + (self.bloodHeight - 2) / 2], [left + blood, top + (self.bloodHeight - 2) / 2], self.bloodHeight - 3)


    '''
    画图方法
    '''
    def draw(self):
        "画图方法"
        # 判断是否为初始化状态
        if self.start:  # 开始状态
            self.moveStop -= 1
            if self.moveStop <= 0:
                self.checkPosition()
                self.start = False
        else:  # 非初始化状态
            # 检测是否在做动作
            if self.attackTimes == 0:
                if self.dieOrAttack != 2:
                    self.dieOrAttack = 0
                    # 检测附近是否有玩家
                    px = self.player.rect.left - self.rect.left
                    py = self.player.rect.top - self.rect.top
                    pz = int(self.mathMonsterToPlayerSpacing())
                    direction = self.mathAngleToDirection(px, py)

                    if pz < self.views:  # 当玩家在视距范围内
                        self.findPlayer = True
                        if pz <= self.attackDistance:
                            self.attack()
                            self.attackTimes += 1
                        else:
                            self.mathFindPlayerMove(direction, abs(px), abs(py), pz)
                    elif self.findPlayer and pz > self.attackDistance and pz < self.views * 2:  # 发现玩家但不在攻击范围内向玩家移动
                        self.mathFindPlayerMove(direction, abs(px), abs(py), pz)
                    elif pz >= self.views * 2 and self.findPlayer:  # 发现玩家后玩家脱离2倍视距之外就停下
                        self.findPlayer = False
                        self.moveY = self.rect.top
                        self.moveX = self.rect.left
                        self.move()
                    else:
                        self.move()
            else:
                if self.attackTimes >= self.attackInterval:
                    self.attackTimes = 0
                else:
                    # 播放攻击动作

                    self.attackTimes += 1

        # 画出怪兽
        if self.dieOrAttack == 2:
            self.playMonsterImage(8)
        else:
            self.playMonsterImage(self.randMoveDirection)
        self.win.blit(self.image, self.rect)
        self.bloodStrip()
        self.drawName()
        self.drawReduceBlood()



# 测试时代码
if __name__ == '__main__':
    pygame.init()
    winsize = (400,400)
    screen = pygame.display.set_mode(winsize, 0, 32)
    pygame.display.set_caption("精灵类测试")

    images = pygame.image.load("../images/test.png").convert()
    columns = 1
    rect = images.get_rect()
    #print(rect)
    width = 40
    height = 40
    frame = (rect.width // width) * (rect.height // height) - 1 #frame = 7
    framerate = pygame.time.Clock()

    inti = 255

    while True:
        framerate.tick(1)
        images.set_alpha(inti)
        inti -= 25
        if inti < 0:
            inti = 255
        # ticks = pygame.time.get_ticks()
        #
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                exit()
        key = pygame.key.get_pressed()
        if key[pygame.K_ESCAPE]:
            exit()
        #
        # if inti < 7:
        #     inti += 1
        # else:
        #     inti = 0
        # rects = (inti * width, 0, width, height)
        # print(rects)
        # image = images.subsurface(rects)
        # aaaaa = pygame.image.load("../images/background.png").convert_alpha()
        screen.fill((0,0,0))
        screen.blit(images, (0, 0))
        pygame.display.flip()



